#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <memory>
#include <random>

enum class FaceValue
{
    _3,
    _4,
    _5,
    _6,
    _7,
    _8,
    _9,
    _10,
    J,
    Q,
    K,
    A,
    _2,
    LITTLE_JOKER,
    BIG_JOKER,
    INVALID, // 无效的点数
};

enum class Suit
{
    HEARTS,   // 红桃
    DIAMONDS, // 方块
    SPADES,   // 黑桃
    CLUBS,    // 梅花
    JOKER,    // 大小王
    INVALID,  // 无效的花色
};

char faceValueToChar(FaceValue face_value)
{
    switch (face_value)
    {
    case FaceValue::_3:
        return '3';
    case FaceValue::_4:
        return '4';
    case FaceValue::_5:
        return '5';
    case FaceValue::_6:
        return '6';
    case FaceValue::_7:
        return '7';
    case FaceValue::_8:
        return '8';
    case FaceValue::_9:
        return '9';
    case FaceValue::_10:
        return 'T';
    case FaceValue::J:
        return 'J';
    case FaceValue::Q:
        return 'Q';
    case FaceValue::K:
        return 'K';
    case FaceValue::A:
        return 'A';
    case FaceValue::_2:
        return '2';
    case FaceValue::LITTLE_JOKER:
        return '#';
    case FaceValue::BIG_JOKER:
        return '$';
    default:
        return '0'; // '0' 表示错误
    }
}

FaceValue charToFaceValue(char c)
{
    switch (c)
    {
    case '3':
        return FaceValue::_3;
    case '4':
        return FaceValue::_4;
    case '5':
        return FaceValue::_5;
    case '6':
        return FaceValue::_6;
    case '7':
        return FaceValue::_7;
    case '8':
        return FaceValue::_8;
    case '9':
        return FaceValue::_9;
    case 'T':
        return FaceValue::_10;
    case 'J':
        return FaceValue::J;
    case 'Q':
        return FaceValue::Q;
    case 'K':
        return FaceValue::K;
    case 'A':
        return FaceValue::A;
    case '2':
        return FaceValue::_2;
    case '#':
        return FaceValue::LITTLE_JOKER;
    case '$':
        return FaceValue::BIG_JOKER;
    }

    return FaceValue::INVALID; // 表示错误
}

struct Card final
{
    Suit      suit       = Suit::INVALID;
    FaceValue face_value = FaceValue::INVALID;

    char getFaceValueText() const
    {
        return faceValueToChar(this->face_value); // 不打印花色
    }

    bool operator<(Card const& other) const
    {
        // enum 类型天然有序，因此可以直接比较大小，比 char 方便多了。
        return this->face_value < other.face_value;
    }
};

enum class HandType
{
    SINGLE,           // 单张
    PAIR,             // 对子
    TRIPLE,           // 三张
    BOMB,             // 炸弹
    ROCKET,           // 王炸
    TRIPLE_1,         // 三带一
    QUAD_2,           // 四带二
    PLANE_2,          // 2 个飞机带翅膀
    PLANE_3,          // 3 个飞机带翅膀
    PLANE_4,          // 4 个飞机带翅膀
    PLANE_5,          // 5 个飞机带翅膀
    SEQUENCE_5,       // 5 张单牌顺子
    SEQUENCE_6,       // 6 张单牌顺子
    SEQUENCE_7,       // 7 张单牌顺子
    SEQUENCE_8,       // 8 张单牌顺子
    SEQUENCE_9,       // 9 张单牌顺子
    SEQUENCE_10,      // 10 张单牌顺子
    SEQUENCE_11,      // 11 张单牌顺子
    SEQUENCE_12,      // 12 张单牌顺子
    PAIR_SEQUENCE_3,  // 3 个对子顺子
    PAIR_SEQUENCE_4,  // 4 个对子顺子
    PAIR_SEQUENCE_5,  // 5 个对子顺子
    PAIR_SEQUENCE_6,  // 6 个对子顺子
    PAIR_SEQUENCE_7,  // 7 个对子顺子
    PAIR_SEQUENCE_8,  // 8 个对子顺子
    PAIR_SEQUENCE_9,  // 9 个对子顺子
    PAIR_SEQUENCE_10, // 10 个对子顺子
};

// 注意成员函数都是纯虚函数，意味着子类必须自己实现这些函数。
struct Hand
{
    int player_idx = -1;

    // 返回当前这手牌的具体类型。
    virtual HandType getType() const = 0;

    // 将当前这一手牌的内容用字符串表示
    virtual std::string getText() const = 0;

    // 判断当前这手牌是否能大过传进来的、用 other 表示的另一手牌。
    virtual bool isBiggerThan(Hand const& other) const = 0;
};

class Single final : public Hand
{
    Card card_;

public:
    Single() = default;

    Single(Card const& card) : card_(card)
    {}

    // 返回当前这手牌的具体类型。
    HandType getType() const override
    {
        return HandType::SINGLE;
    }

    // 将当前这一手牌的内容用字符串表示
    std::string getText() const override
    {
        auto s = std::string{};
        s.push_back(card_.getFaceValueText());
        return s;
    }

    // 判断当前这手牌是否能大过传进来的、用 other 表示的另一手牌。
    bool isBiggerThan(Hand const& other) const override
    {
        if (other.getType() == HandType::SINGLE)
        {
            return static_cast<Single const&>(other).card_ < this->card_;
        }

        return false;
    }
};

class Pair final : public Hand
{
    Card cards_[2];

public:
    Pair() = default;

    Pair(Card const& card_1, Card const& card_2) : cards_{card_1, card_2}
    {}

    // 返回当前这手牌的具体类型。
    HandType getType() const override
    {
        return HandType::PAIR;
    }

    // 将当前这一手牌的内容用字符串表示
    std::string getText() const override
    {
        auto s = std::string{};
        s.push_back(cards_[0].getFaceValueText());
        s.push_back(cards_[1].getFaceValueText());
        return s;
    }

    // 判断当前这手牌是否能大过传进来的、用 other 表示的另一手牌。
    bool isBiggerThan(Hand const& other) const override
    {
        if (other.getType() == HandType::PAIR)
        {
            return this->cards_[0] < static_cast<Pair const&>(other).cards_[0];
        }

        return false;
    }
};

enum class PlayerRole
{
    LANDLORD,
    LEFT_PEASANT,
    RIGHT_PEASANT,
    INVALID,
};

class Player final
{
    PlayerRole        role_ = PlayerRole::INVALID;
    std::vector<Card> remaining_cards_;

public:
    Player() = default;

    void setRole(PlayerRole role)
    {
        this->role_ = role;
    }

    std::string getRole() const
    {
        switch (this->role_)
        {
        case PlayerRole::LANDLORD:
            return "M";
        case PlayerRole::LEFT_PEASANT:
            return "L";
        case PlayerRole::RIGHT_PEASANT:
            return "R";
        default:
            return "InvalidRole";
        }
    }

    std::string getTextOfRemainingCards() const
    {
        auto s = std::string{};
        for (auto card : this->remaining_cards_)
        {
            s.push_back(card.getFaceValueText());
        }

        return s;
    }

    std::size_t getRemainingCardCount() const
    {
        return this->remaining_cards_.size();
    }

    void accept(Card const& card)
    {
        this->remaining_cards_.push_back(card);
    }

    // decide 是最复杂、最重要的 AI 函数，这里做了最简化处理。
    std::shared_ptr<Hand> decide(std::shared_ptr<Hand> sp_prev_hand)
    {
        auto sp_ret = std::shared_ptr<Hand>{};

        if (!sp_prev_hand)
        {
            // prev_hand 为空指针，说明轮到第一家出牌。
            // 直接出一个最简单的单张。
            auto tmp_hand = Single(remaining_cards_.back());
            sp_ret        = std::make_shared<Single>(tmp_hand);
            remaining_cards_.pop_back(); // 删除已经打出去的牌

            return sp_ret;
        }

        if (sp_prev_hand->getType() == HandType::SINGLE)
        {
            auto tmp_hand = Single(remaining_cards_.back());
            if (tmp_hand.isBiggerThan(*sp_prev_hand))
            {
                // 走到这里说明要得起
                sp_ret = std::make_shared<Single>(tmp_hand);
                remaining_cards_.pop_back(); // 删除已经打出去的牌

                return sp_ret; // 返回打出去的这手牌
            }
        }

        return sp_ret; // 要不起
    }
};

class Deck final
{
    Card cards_[54];

public:
    Deck()
    {
        auto i = 0;
        for (auto suit = Suit::HEARTS; suit <= Suit::CLUBS;
             reinterpret_cast<int&>(suit)++)
        {
            for (auto face_value = FaceValue::_3; face_value <= FaceValue::_2;
                 reinterpret_cast<int&>(face_value)++)
            {
                cards_[i].suit       = suit;
                cards_[i].face_value = face_value;
                ++i;
            }
        }

        cards_[i].suit       = Suit::JOKER;
        cards_[i].face_value = FaceValue::LITTLE_JOKER;

        i++;

        cards_[i].suit       = Suit::JOKER;
        cards_[i].face_value = FaceValue::BIG_JOKER;
    }

    void shuffle()
    {
        std::random_device rd{};
        auto               g = std::mt19937(rd());
        std::shuffle(std::begin(this->cards_), std::end(this->cards_), g);
    }

    void getPlayerCards(std::vector<Card>& player_1,
                        std::vector<Card>& player_2,
                        std::vector<Card>& player_3,
                        std::vector<Card>& last_3_cards)
    {
        for (auto i = 0; i < 17; i++)
        {
            player_1.push_back(this->cards_[i]);
        }

        for (auto i = 17; i < 34; i++)
        {
            player_2.push_back(this->cards_[i]);
        }

        for (auto i = 34; i < 51; i++)
        {
            player_3.push_back(this->cards_[i]);
        }

        for (auto i = 51; i < 54; i++)
        {
            last_3_cards.push_back(this->cards_[i]);
        }
    }
};

class Game final
{
    Deck              deck_;
    Player            players_[3];
    std::vector<Card> last_3_cards_;
    int               landlord_idx      = -1;
    int               right_peasant_idx = -1;
    int               left_peasant_idx  = -1;
    int               current_idx = -1; // 保存当前出牌玩家的下标

    std::vector<std::shared_ptr<Hand>> history_;

public:
    Game() = default;

    // 抢地主
    int bidForLandlord() const
    {
        return std::rand() % 3; // 随机选一个当地主
    }

    void run()
    {
        this->deck_.shuffle();

        auto cards_0 = std::vector<Card>{};
        auto cards_1 = std::vector<Card>{};
        auto cards_2 = std::vector<Card>{};

        this->deck_.getPlayerCards(
            cards_0, cards_1, cards_2, this->last_3_cards_);

        for (auto card : cards_0)
        {
            this->players_[0].accept(card);
        }

        for (auto card : cards_1)
        {
            this->players_[1].accept(card);
        }

        for (auto card : cards_2)
        {
            this->players_[2].accept(card);
        }

        landlord_idx      = this->bidForLandlord();
        right_peasant_idx = (landlord_idx + 1) % 3;
        left_peasant_idx  = (landlord_idx + 2) % 3;

        this->players_[landlord_idx].setRole(PlayerRole::LANDLORD);
        this->players_[right_peasant_idx].setRole(PlayerRole::RIGHT_PEASANT);
        this->players_[left_peasant_idx].setRole(PlayerRole::LEFT_PEASANT);

        // 把 3 张底牌发给地主
        for (auto card : this->last_3_cards_)
        {
            this->players_[landlord_idx].accept(card);
        }

        std::cout << std::endl;
        std::cout << "M-->: "
                  << this->players_[landlord_idx].getTextOfRemainingCards()
                  << std::endl;
        std::cout << "R-->: "
                  << this->players_[right_peasant_idx].getTextOfRemainingCards()
                  << std::endl;
        std::cout << "L-->: "
                  << this->players_[left_peasant_idx].getTextOfRemainingCards()
                  << std::endl;
        std::cout << std::endl;

        current_idx       = landlord_idx;            // 地主最先出牌
        auto sp_prev_hand = std::shared_ptr<Hand>{}; // 保存前一手牌的指针

        while (true)
        {
            auto& cur_player = this->players_[current_idx];

            if (!history_.empty() && current_idx == history_.back()->player_idx)
            {
                std::cout << std::endl;
                sp_prev_hand = {};
            }

            auto sp_hand = cur_player.decide(sp_prev_hand);
            if (sp_hand)
            {
                std::cout << cur_player.getRole() << ": " << sp_hand->getText()
                          << std::endl;

                // 走到这里表示要得起，先将出的牌保存在历史记录中。
                sp_hand->player_idx = current_idx;
                history_.push_back(sp_hand);
                if (cur_player.getRemainingCardCount() == 0)
                {
                    // 当前玩家出完这手牌后就没牌了，说明赢了。
                    std::cout << std::endl;
                    std::cout << "*********************" << std::endl;
                    std::cout << "*     Winner: " << cur_player.getRole()
                              << "     *" << std::endl;
                    std::cout << "*********************" << std::endl;
                    std::cout << std::endl;
                    return; // 本局游戏结束
                }
            }
            else
            {
                // 走到这里表示要不起
                std::cout << cur_player.getRole() << ": " << std::endl;
            }

            // 保存本次出牌得指针，以便下家知道该怎么应对。
            if (sp_hand)
            {
                sp_prev_hand = sp_hand;
            }

            // 将出牌机会让给下家
            current_idx = (current_idx + 1) % 3;
        }
    }
};

int main()
{
    auto game = Game{};

    while (true)
    {
        std::cout << "********************" << std::endl;
        std::cout << "*     New Game     *" << std::endl;
        std::cout << "********************" << std::endl;

        game.run();
        std::cout << "Do you want to continue? [y/n]" << std::endl;

        auto c = char{};
        std::cin >> c;

        if ('y' != c)
        {
            std::cout << "Game Over!" << std::endl;
            return 0;
        }
    }
}